#include <Arduino.h>
#include <plc_config.h>

#include <plc_diag.h>
#include <plc_rtc.h>
#include <plc_hw.h>
#include "hwboard.h"

#define PLC_BKP_RTC_IS_OK PLC_BKP_RTC_IS_OK_OFFSET

volatile uint8_t plc_rtc_failure = 0; //Not failed by default

uint32_t plc_rtc_clken_and_check(void)
{
    // return LL_RTC_BKP_GetRegister(BKP, PLC_BKP_RTC_IS_OK);
}

static bool start_flg = true;

void plc_rtc_init(tm *time)
{
    uint32_t tmp = 0;
    board.rtc_get();
    // uint32_t year;
    // uint32_t i;

    // start_flg = true;

    // __HAL_RCC_PWR_CLK_ENABLE();

    // HAL_PWR_EnableBkUpAccess();
    // HAL_PWR_EnableBkUpAccess();
    // LL_RTC_BKP_SetRegister(BKP, PLC_BKP_RTC_IS_OK, 0);

    // /* LSE oscillator clock used as the RTC clock */
    // RCC_BDCR |= 0x00000100;
    // RCC_BDCR |= RCC_BDCR_LSEON;

    // __HAL_RCC_RTC_CLK_ENABLE();

    // rtc_unlock();

    // RTC_ISR |= RTC_ISR_INIT;
    // for (i = 0; i < 1000000; i++)
    // {
    //     if (RTC_ISR & RTC_ISR_INITF)
    //     {
    //         break;
    //     }
    // }

    // if (!(RTC_ISR & RTC_ISR_INITF))
    // {
    //     plc_diag_status |= PLC_DIAG_ERR_LSE;

    //     RTC_ISR &= ~RTC_ISR_INIT;
    //     pwr_enable_backup_domain_write_protect();
    //     return;
    // }

    // rtc_set_prescaler(0x1FF, 0x3F);

    // tmp = (unsigned long)(time->tm_sec % 10);
    // tmp |= ((unsigned long)(time->tm_sec / 10)) << 4;
    // tmp |= ((unsigned long)(time->tm_min % 10)) << 8;
    // tmp |= ((unsigned long)(time->tm_min / 10)) << 12;
    // tmp |= ((unsigned long)(time->tm_hour % 10)) << 16;
    // tmp |= ((unsigned long)(time->tm_hour / 10)) << 20;
    // RTC_TR = tmp;

    // /* Only 2 digits used!!! *time may be const, so use year var. */
    // year = time->tm_year % 100;

    // tmp = (unsigned long)(time->tm_day % 10);
    // tmp |= ((unsigned long)(time->tm_day / 10)) << 4;
    // tmp |= ((unsigned long)(time->tm_mon % 10)) << 8;
    // tmp |= ((unsigned long)(time->tm_mon / 10)) << 12;
    // tmp |= ((unsigned long)(year % 10)) << 16;
    // tmp |= ((unsigned long)(year / 10)) << 20;
    // RTC_DR = tmp;

    // /* exit from initialization mode */
    // RTC_ISR &= ~RTC_ISR_INIT;

    // LL_RTC_BKP_SetRegister(BKP, PLC_BKP_RTC_IS_OK, 1);

    // pwr_enable_backup_domain_write_protect();
}

void plc_rtc_dt_set(tm *time)
{
    uint32_t i;
    uint32_t tr;
    uint32_t dr;

    if (plc_diag_status & PLC_DIAG_ERR_LSE)
    {
        return;
    }

    board.wyear = time->tm_year;
    board.wmonth = time->tm_mon;
    board.wday = time->tm_mday;
    board.whour = time->tm_hour;
    board.wminute = time->tm_min;
    board.wsecond = time->tm_sec;
    board.rtc_setup();
    return;
    /* LSE error occured */
lse_error:
    plc_diag_status |= PLC_DIAG_ERR_LSE;
    //     rtc_lock();
    //     pwr_enable_backup_domain_write_protect();
}
/*
 *  Julian Day Number computation
 *  See http://en.wikipedia.org/wiki/Julian_day
 */
static uint32_t jdn(tm *date_time)
{
    uint8_t a, m;
    uint16_t y;
    uint32_t ret;

    /* Compute helper factors */
    a = (14 - (uint8_t)date_time->tm_mon) / 12;
    m = (uint8_t)date_time->tm_mon + 12 * a - 3;
    y = (uint16_t)date_time->tm_year + 4800 - a;

    /* Julian day number computation */
    ret = (uint32_t)date_time->tm_mday;
    ret += (153ul * (uint32_t)m + 2ul) / 5ul;
    ret += 365ul * (uint32_t)y;
    ret += (uint32_t)(y / 4);
    ret -= (uint32_t)(y / 100);
    ret += (uint32_t)(y / 400);
    ret -= 32045ul;

    return ret;
}

/*
 * Compute unix-time seconds
 */
static uint32_t dt_to_sec(tm *date_time)
{
    uint32_t ret;

    /*Count days from 01.01.1970*/
    ret = jdn(date_time);
    ret -= 2440588ul;

    /*Convert day number to seconds*/
    ret *= 24ul;
    ret += date_time->tm_hour;
    ret *= 60ul;
    ret += date_time->tm_min;
    ret *= 60ul;
    ret += date_time->tm_sec;

    return ret;
}

#define PLC_BASE_TICK_NS (100000)
#define PLC_RTC_CORR_THR (1200000000)
static int32_t plc_rtc_tick_ns;    // Initial tick value is 100'000ns
static int64_t plc_rtc_correction; //Correction to RTC time

static uint32_t plc_rtc_dr = 0;
static uint32_t plc_rtc_tr = 0;


///WARNING, this function is not reentrant!!!
void plc_rtc_time_get(IEC_TIME *current_time)
{
    static tm curr;
    static uint32_t rtc_ssr, rtc_tr, rtc_dr;
    static int64_t rtc_corr;

    //Read buffered register values
    PLC_DISABLE_INTERRUPTS();
    rtc_tr = plc_rtc_tr;
    rtc_dr = plc_rtc_dr;
    rtc_corr = plc_rtc_correction;
    PLC_ENABLE_INTERRUPTS();

    /* Convert current date/time to unix time seconds */
    current_time->tv_sec = millis() / 1000;
    current_time->tv_sec += (rtc_corr / 1000000000);
    current_time->tv_nsec = (millis() % 1000) * 1000000 + (rtc_corr % 1000000000);
}
